//Copyright (c) Microsoft Corporation.  All rights reserved.

using System;
using System.Collections.Generic;
using System.Text;
using Eas.WebCrawler.Interfaces;
using System.Net;

namespace Eas.WebCrawler.Model.Gateway {
    
    /// <summary>The main production implementation of the <see cref="WebGateway"/> interface.</summary>
    public class BaseWebGateway : WebGateway
    {
        private const string TextMimeTypePrefix = "text/";
        
        /// <summary>Constructs a <see cref="BaseWebGateway"/> without any arguments.</summary>
        public BaseWebGateway() { }

        #region WebGateway Members

        /// <summary>Creates a new <see cref="WebRequestInterface"/> for the passed-in request.</summary>
        /// <param name="processingState">A <see cref="ProcessingState"/> whose <code>Request</code> property contains data about what uri to use for the created request pbject.</param>
        /// <returns>A new <see cref="WebRequestInterface"/> wrapping a <see cref="System.Net.WebRequest"/> object.</returns>
        public virtual WebRequestInterface CreateWebRequest(ProcessingState processingState) {
            return new BaseWebRequest(System.Net.WebRequest.Create(processingState.Request.Uri));
        }

        /// <summary>Calls into BeginGetResponse on the <see cref="WebRequestInterface"/> contained in the passed in <paramref name="processingState"/>.</summary>
        /// <param name="processingState">A <see cref="ProcessingState"/> whose <see cref="ProcessingState.WebRequest"/> property has been filled in.</param>
        /// <param name="callback">The callback function to call, if any, when GetResponse completes.</param>
        /// <param name="state">State passed into the callback for asynchronous processing.</param>
        /// <returns>An <see cref="IAsyncResult"/>.</returns>
        public virtual IAsyncResult BeginGetResponse(ProcessingState processingState, AsyncCallback callback, object state) {
            return processingState.WebRequest.BeginGetResponse(callback, state);
        }

        /// <summary>Returns a <see cref="WebResponseInterface"/>created by the completion of a GetResponse action.</summary>
        /// <param name="processingState">A <see cref="ProcessingState"/> whose <see cref="ProcessingState.WebRequest"/> property has been filled in.</param>
        /// <param name="result">A <see cref="WebResponseInterface"/> wrapping a <see cref="System.Net.WebResponse"/> object.</param>
        /// <returns></returns>
        public virtual WebResponseInterface EndGetResponse(ProcessingState processingState, IAsyncResult result) {
            return processingState.WebRequest.EndGetResponse(result);
        }

        /// <summary>Looks at the <see cref="WebResponseInterface"/> in the passed-in <paramref name="processingState"/> and detects if the response is a redirect.</summary>
        /// <param name="processingState">A <see cref="ProcessingState"/> whose <see cref="ProcessingState.WebResponse"/> property has been filled in.</param>
        /// <returns><code>true</code> if the returned response is a redirect, otherwise <code>false</code>.</returns>
        public virtual bool IsRedirect(ProcessingState processingState) {
            if (!(processingState.WebResponse is System.Net.HttpWebResponse)) return false;

            System.Net.HttpWebResponse webResponse = processingState.WebResponse.WebResponse as System.Net.HttpWebResponse;

            if (webResponse == null) return false;

            return (webResponse.StatusCode == System.Net.HttpStatusCode.Moved
                       || webResponse.StatusCode == System.Net.HttpStatusCode.MovedPermanently
                       || webResponse.StatusCode == System.Net.HttpStatusCode.Redirect
                       || webResponse.StatusCode == System.Net.HttpStatusCode.RedirectKeepVerb
                       || webResponse.StatusCode == System.Net.HttpStatusCode.RedirectMethod);
        }

        /// <summary>Retrieves the redirect Uri from a web response representing a redirect</summary>
        /// <param name="processingState">A <see cref="ProcessingState"/> whose <see cref="ProcessingState.WebResponse"/> property has been filled in.</param>
        /// <returns>The <see cref="System.Uri"/> which is the target of the redirect.</returns>
        public virtual Uri GetRedirectUri(ProcessingState processingState) {
            return new Uri(processingState.WebResponse.Headers["Location"], UriKind.RelativeOrAbsolute);
        }

        /// <summary>Retrieves the response stream from a <see cref="WebResponseInterface"/>.</summary>
        /// <param name="processingState">A <see cref="ProcessingState"/> whose <see cref="ProcessingState.WebResponse"/> property has been filled in.</param>
        /// <returns>A new <see cref="System.IO.Stream"/> which contains the body of the response.</returns>
        public virtual System.IO.Stream GetResponseStream(ProcessingState processingState) {
            return processingState.WebResponse.GetResponseStream();
        }

        /// <summary>Gets the http response code for a failed web request.</summary>
        /// <param name="exception">The <see cref="System.Exception"/> thrown when the request failed.</param>
        /// <returns>A number representing the http response code for the failure.</returns>
        /// <example>If the request failed because the requested Uri could not be found, this function will return <code>404</code>.</example>
        public virtual int GetResponseCode(System.Net.WebException exception) {
            if (exception.Status == System.Net.WebExceptionStatus.ProtocolError && exception.Response != null && exception.Response is System.Net.HttpWebResponse) {
                return (int)(((System.Net.HttpWebResponse)exception.Response).StatusCode);
            }
            else {
                return 500;
            }
        }

        /// <summary>An asynchronous function which reads data from a stream that was returned from a <see cref="WebResponseInterface"/> (though to be honest, it would perform the read on any stream).</summary>
        /// <param name="processingState">A <see cref="ProcessingState"/> whose <see cref="ProcessingState.ResponseStream"/> property has been filled in.</param>
        /// <param name="buffer">The byte array into which the stream will write out it's contents.</param>
        /// <param name="offset">The offset into the <paramref name="buffer"/> at which to write out the stream's contents.</param>
        /// <param name="count">The maximum number of bytes that can be written into the <paramref name="buffer"/></param>
        /// <param name="callback">The callback function to call, if any, when GetResponse completes.</param>
        /// <param name="state">State passed into the callback for asynchronous processing.</param>
        /// <returns>The asynchronous result token for the request.</returns>
        public virtual IAsyncResult BeginReadStream(ProcessingState processingState, byte[] buffer, int offset, int count, AsyncCallback callback, object state) {
            return processingState.ResponseStream.BeginRead(buffer, offset, count, callback, state);
        }

        /// <summary>The completing function of <see cref="BeginReadStream"/>.</summary>
        /// <param name="processingState">A <see cref="ProcessingState"/> whose <see cref="ProcessingState.ResponseStream"/> property has been filled in.</param>
        /// <param name="result">The <see cref="IAsyncResult"/> returned from the begin method.</param>
        /// <returns>The number of bytes read.</returns>
        public virtual int EndReadStream(ProcessingState processingState, IAsyncResult result) {
            return processingState.ResponseStream.EndRead(result);
        }

        /// <summary>Determines if the contents of the web response is text (like html) or binary (like images and movies).</summary>
        /// <param name="processingState">A <see cref="ProcessingState"/> whose <see cref="ProcessingState.WebResponse"/> property has been filled in.</param>
        /// <returns><code>true</code> if the uri points to text, otherwise <code>false</code>.</returns>
        public virtual bool ResponseIsText(ProcessingState processingState) {
            return processingState.WebResponse.ContentType.StartsWith( TextMimeTypePrefix, StringComparison.OrdinalIgnoreCase);
        }

        /// <summary>Tells System.Net how many active connections to one end point can be open at the same time.</summary>
        /// <param name="limit">The number of active connections to one end point can be open at the same time.</param>
        public virtual void SetConnectionLimit(int limit) {
            System.Net.ServicePointManager.DefaultConnectionLimit = limit;
        }

        #endregion

    }
}
